﻿namespace FlexPlatform.DataModel.DataAccess.Identification
{
    using System.Linq.Expressions;
    using FlexPlatform.Contracts.DataModel.Identification;
    using System.Collections.ObjectModel;
    using FlexPlatform.Contracts.DataModel;
    using FlexPlatform.GeneralUse;
    using System.Collections.Generic;
    using System;

    public class IdentifyingFieldGroup<T> : IIdentifyingFieldGroup<T> where T : IDbEntity
    {

        #region Private fields & Constructors

        private Dictionary<string, LambdaExpression> clsFields;
        private int clsPriority;

        internal IdentifyingFieldGroup(int priority)
        {
            clsFields = new Dictionary<string, LambdaExpression>();
            clsPriority = priority;
        }

        #endregion

        #region Field Management

        public void AddField(Expression<Func<T, dynamic>> property)
        {
            var propertyName = ReflectionHelper.GetPropertyName(property);
            clsFields.Add(propertyName, property);
        }

        public bool ContainsField(string fieldName)
        {
            return clsFields.ContainsKey(fieldName);
        }

        #endregion

        public int Priority
        {
            get
            {
                return clsPriority;
            }
        }

        public bool CanIdentify(T dataEntity)
        {
            // check if all required fields have data
            foreach (var fieldLambda in clsFields.Values)
            {
                object[] parameters = new object[] { dataEntity };
                var fieldValue = fieldLambda.Compile().DynamicInvoke(parameters);
                if (fieldValue == null)
                {
                    return false;
                }
            }

            return true;
        }

        public Func<T, bool> GetFilter(T dataEntity)
        {
            return (Func<T, bool>)GetFilterExpression(dataEntity).Compile();
        }


        private LambdaExpression GetFilterExpression(T dataEntity)
        {
            const string constInstanceLetter = "i";

            var variable = Expression.Parameter(dataEntity.GetType(), constInstanceLetter);
            Expression filterExpression = null;
            
            foreach (var fieldLambda in clsFields.Values)
            {
                object[] parameters = new object[] { dataEntity } ;
                var fieldValue = Expression.Constant(fieldLambda.Compile().DynamicInvoke(parameters));

                var property = Expression.Property(variable, ReflectionHelper.GetPropertyName(fieldLambda));

                var fieldCondition = Expression.Equal(property, fieldValue);
                if (filterExpression == null)
                {
                    filterExpression = fieldCondition;
                }
                else
                {
                    filterExpression = Expression.AndAlso(fieldCondition, filterExpression);
                }
            }

            return Expression.Lambda(filterExpression, variable);
        }
    }
}
